package com.power.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.power.constant.BusinessEnum;
import com.power.constant.HttpConstants;
import com.power.constant.ResourceConstants;
import com.power.filter.TokenTranslationFilter;
import com.power.model.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Description Spring Security相关配置
 * @Author dingchunlin
 * @Date 2024/07/12 14:27
 * @Version 1.0.0
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ReousceServiceConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private TokenTranslationFilter tokenTranslationFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 关闭跨站请求
        http.csrf().disable();
        // 关闭跨域请求
        http.cors().disable();
        // 关闭Session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 编写一个token解析器，将token解析为用户信息，并将用户信息放入SecurityContextHolder中
        http.addFilterBefore(tokenTranslationFilter, UsernamePasswordAuthenticationFilter.class);

        // 异常处理
        http.exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint()) // 未登录时返回401
                .accessDeniedHandler(accessDeniedHandler()); // 未登录或无权限访问时返回401或403

        // 配置其他请求
        http.authorizeRequests()
                .antMatchers(ResourceConstants.RESOURCE_ALLOW_URLS)
                .permitAll()
                .anyRequest()
                .authenticated(); // 除了需要放行的请求，其他请求都需要登录
    }

    /**
     * 请求没有携带Token
     * @return
     */
    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                //设置响应头信息
                response.setContentType(HttpConstants.APPLICATION_JSON);
                response.setCharacterEncoding(HttpConstants.UTF_8);

                // 响应错误信息
                Result<Object> result = Result.fail(BusinessEnum.UN_AUTHORIZATION);
                ObjectMapper objectMapper = new ObjectMapper();
                response.getWriter().write(objectMapper.writeValueAsString(result));
                response.getWriter().flush();
                response.getWriter().close();
            }
        };
    }

    /**
     * 处理请求携带Token，但是权限不足的情况
     * @return
     */
    @Bean
    public AccessDeniedHandler accessDeniedHandler() {
        return new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                //设置响应头信息
                response.setContentType(HttpConstants.APPLICATION_JSON);
                response.setCharacterEncoding(HttpConstants.UTF_8);

                // 响应错误信息
                Result<Object> result = Result.fail(BusinessEnum.ACCESS_DENY_FAIL);
                ObjectMapper objectMapper = new ObjectMapper();
                response.getWriter().write(objectMapper.writeValueAsString(result));
                response.getWriter().flush();
                response.getWriter().close();
            }
        };
    }
}
